package com.ct.commons.interceptor.auth;

import com.ct.application.sysuser.contants.SysUserContants;
import com.ct.application.sysuser.model.vo.SysUserDetailVO;
import com.ct.application.sysuser.service.ISystemUserService;
import com.ct.commons.annotation.LoginIgnore;
import com.ct.commons.utils.WebUtils;
import com.ct.rbac.enums.RbacResultEnum;
import com.ct.redis.util.RedisUtil;
import com.ct.utils.JsonUtil;
import com.ct.utils.exception.ApiException;
import com.ct.utils.jwt.JwtUtil;
import com.ct.utils.jwt.UserInfo;
import com.ct.utils.result.R;
import com.ct.utils.threadlocal.UserThreadLocal;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import java.lang.annotation.Annotation;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

@Component
@Slf4j
public class AdminLoginInterceptor implements HandlerInterceptor {

    @Resource
    private RedisUtil<String> redisUtil;
    @Autowired
    private ISystemUserService systemUserService;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        response.setHeader("x-frame-options", "SAMEORIGIN");
        response.setHeader("Set-Cookie", "SameSite=None; Secure");
        if (handler instanceof HandlerMethod) {
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            // 判断是否执行的类增加忽略注解
            LoginIgnore annotation = handlerMethod.getBeanType().getAnnotation(LoginIgnore.class);
            if (annotation != null) {
                return true;
            }
            // 判断类上加了注解
            Annotation methodAnnotation = handlerMethod.getMethodAnnotation(LoginIgnore.class);
            if (methodAnnotation == null) {
                // 判断token是否存在
                String token = request.getHeader("token");
                if (StringUtils.isEmpty(token)) {
                    throw new ApiException(RbacResultEnum.TOKEN_LOGIN_BEN);
                }

                // 判断token是否合法 ----解析token
                UserInfo userInfo = JwtUtil.analyzeToken(token);
                if (Objects.isNull(userInfo)) {
                    throw new ApiException(RbacResultEnum.TOKEN_LOGIN_BEN_OK);
                }

                // 临界时间比对 单位是s
                Long expireTime = redisUtil.getExpireTime(SysUserContants.LOGIN_REDIS_TOKEN_KEY + userInfo.getId());
                if (expireTime < JwtUtil.ONE_EXPIRE_TIME) {
                    // 这里就会产生一个全新的60s中的新的token
                    Map<String, String> stringStringMap = JwtUtil.generateToken(userInfo);
                    // 新老更替
                    // 把redis中的token更换掉
                    String newToken = stringStringMap.get("token");
                    String refreshTime = stringStringMap.get("refreshTime");
                    redisUtil.set(SysUserContants.LOGIN_REDIS_TOKEN_KEY + userInfo.getId(), newToken, JwtUtil.EXPIRE_TIME, TimeUnit.MILLISECONDS);
                    userInfo.setRenwalTime(refreshTime);
                    userInfo.setToken(newToken);
                    // 返回客户端
                    WebUtils.renderString(response, JsonUtil.obj2String(R.successToken(userInfo)));
                }

                // 判断是否需要续期
                String redisBlackListToken = redisUtil.get(SysUserContants.LOGIN_REDIS_BLACKLIST_TOKEN_KEY + userInfo.getId());
                if (StringUtils.isNotEmpty(redisBlackListToken) && redisBlackListToken.equalsIgnoreCase(token)) {
                    throw new ApiException(RbacResultEnum.TOKEN_LOGIN_BEN_WX);
                }

                // 挤下线
//                String redisToken = redisUtil.get(SysUserContants.LOGIN_REDIS_TOKEN_KEY + userInfo.getId());
//                if (StringUtils.isEmpty(redisToken)) {
//                    throw new ApiException(RbacResultEnum.TOKEN_LOGIN_BEN_OK);
//                }
//
//                // 挤下线---具体判断
//                if (!token.equalsIgnoreCase(redisToken)) {
//                    throw new ApiException(RbacResultEnum.NEED_NOSAME_LOGIN);
//                }

                // 线程副本存放用户信息
                UserThreadLocal.set(userInfo);

                SysUserDetailVO sysUserDetailVO = systemUserService.selectById(userInfo.getId());
                if (Objects.isNull(sysUserDetailVO)) {
                    throw new ApiException(RbacResultEnum.ACCOUNT_EXITS);
                }
                // 判断用户是否激活
                if (sysUserDetailVO.getStatus() != null && sysUserDetailVO.getStatus().equals(0)) {
                    throw new ApiException(RbacResultEnum.NEED_LOGIN_BEN);
                }
                // 判断用户是否删除
                if (sysUserDetailVO.getIsDeleted() != null && sysUserDetailVO.getIsDeleted().equals(1)) {
                    throw new ApiException(RbacResultEnum.ACCOUNT_EXITS);
                }

            }
        }else{
            String systemId = request.getParameter("systemId");
            if(StringUtils.isNotEmpty(systemId)) {
                UserInfo userInfo = new UserInfo();
                userInfo.setSystemId(Long.parseLong(systemId));
                UserThreadLocal.set(userInfo);
            }
        }
        // 请求资源
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        if (!Objects.isNull(UserThreadLocal.get())) {
            UserThreadLocal.remove();
        }
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        if (!Objects.isNull(UserThreadLocal.get())) {
            UserThreadLocal.remove();
        }
    }
}
